<?php

namespace App\Util;

use App\Component\UserIdentity\BackendUser;
use App\Core\Exception\DefaultException;
use EasySwoole\Component\Di;
use EasySwoole\Mysqli\QueryBuilder;
use EasySwoole\ORM\DbManager;

class CommonHelper{

    /**
     * 生成密码
     * @param $pwd
     * @param $salt
     * @return string
     */
    public static function pwdEncrypt($pwd, $salt){
        return md5($salt . md5(strrev($salt) . md5($pwd)));
    }

    /**
     * 获取当前时间
     * @return false|string
     */
    public static function currentTime(){
        return date('Y-m-d H:i:s', time());
    }

    /**
     * SQL防注入处理，将双引号，单引号等换成HTML代码
     * @param $value
     * @param bool $doubleEncode
     * @return array|mixed|string
     */
    public static function encode($value, $doubleEncode = true){
        if(empty($value)){
            return $value;
        }
        if(is_array($value)){
            foreach(array_keys($value) as $key){
                $var[$key] = static::encode($value[$key], $doubleEncode);
            }

            return $var;
        }

        return htmlspecialchars($value, ENT_QUOTES | ENT_SUBSTITUTE, 'UTF-8', $doubleEncode);
    }

    /**
     * 把返回的数据集转换成树结构
     * @param array $list 要转换的数据集
     * @param string $pk 主键
     * @param string $pid parent标记字段
     * @param string $child 子类数组标识
     * @param int $root 起始父级ID
     * @return array
     */
    public static function listToTree($list, $pk = 'id', $pid = 'pid', $child = '_', $root = 0, $maxlevel = null){
        $tree = [];
        if(is_array($list)){
            // 创建基于主键的数组引用
            $refer = [];
            foreach($list as $key => $data){
                $refer[$data[$pk]] =& $list[$key];
            }
            foreach($list as $key => $data){
                // 判断是否存在parent
                $parentId = $data[$pid];
                $level = self::findParentLevel($refer, $parentId, $pid);
                if($maxlevel === null || !is_numeric($maxlevel) || ($level < $maxlevel)){
                    $list[$key]['__lvl'] = $level;
                    if($root == $parentId){
                        $tree[] =& $list[$key];
                    }else{
                        if(isset($refer[$parentId])){
                            $parent =& $refer[$parentId];
                            $parent[$child][] =& $list[$key];
                        }
                    }
                }
            }
        }

        return $tree;
    }

    public static function findParentLevel($refer, $parent_id = 0, $pid = 'pid'){
        $level = 0;
        while(isset($refer[$parent_id])){
            $level++;
            $parent_id = $refer[$parent_id][$pid];
        }
        return $level;
    }

    /**
     * 合并树结构数据为列表数据
     * @param $list
     * @param string $show
     * @return array
     */
    public static function mergeTree($list, $show = 'title'){
        $res = [];
        if(is_array($list)){
            $count = count($list);
            for($i = 0; $i < $count; $i++){
                $v = $list[$i];
                $clist = [];
                if(isset($v['_'])){
                    $clist = self::mergeTree($v['_'], $show);
                    unset($v['_']);
                }
                $str = '';
                if($v['__lvl']){
                    $str = str_repeat("┗", ($v['__lvl']-1) * 1) . ($i == $count - 1 ? '┗' : '┣') . ' ';
                }
                $v['_' . $show] = $str . $v[$show];

                $res[] = $v;
                $res = array_merge($res, $clist);
            }

        }
        return $res;
    }

    /**
     * 把数据集转换成顺序式，父级->子级数据，放在同一层级上
     * @param array $list 要转换的数据集
     * @param string $pk 主键
     * @param string $pid parent标记字段
     * @param int $root 起始父ID
     * @param string $show 转换字段
     * @return array
     */
    public static function listToOneLevel($list, $pk = 'id', $pid = 'pid', $root = 0, $show = 'name', $max = null, $child = '_'){
        return self::mergeTree(self::listToTree($list, $pk, $pid, $child, $root, $max), $show);
    }

    /**
     * 判断是否为唯一索引冲突异常
     * @param \Throwable $exception
     * @return bool
     */
    public static function isDuplicateEx(\Throwable $exception){
        return strpos($exception->getMessage(), '1062 Duplicate entry') !== false;
    }

    /**
     * 字段Md5值，用于作为唯一键存储使用
     * @param mixed ...$fields
     * @return string
     */
    public static function fieldMd5Value(...$fields){
        $string = implode('', $fields);
        return md5($string);
    }

    /**
     * 值加引号
     * @param $value
     * @return mixed|string
     */
    public static function quoteValue($value){
        if(!is_string($value)){
            return $value;
        }
        return "'" . addcslashes(str_replace("'", "''", $value), "\000\n\r\\\032") . "'";
    }

    public static function md5FromArray(array $array){
        return md5(implode('_', $array));
    }

    /**
     * 获取扩展名
     * @param $file
     * @return false|string
     */
    public static function getExtension($file){
        return substr(strrchr($file, '.'), 1);
    }

    /**
     * 生成发货单ID
     * @return string
     */
    public static function makeOrderID(){
        return date('YmdHis', time()) . sprintf("%03d", intval(floatval(microtime()) * 1000)) . sprintf("%03d", mt_rand(0, 999));
    }

    public static function getRandom($prefix = '', $length = 12){
        $randLen = 5;

        //少于5位的随机数时，直接使用random_int方法
        if($length < $randLen){
            $max = intval(str_repeat('9', $length));
            $random = str_pad(random_int(0, $max), $length, '0', STR_PAD_LEFT);
        }else{
            $random = base_convert(substr(uniqid(), 0 - $length), 16, 10);
            if(strlen($random) > $length){
                $random = substr($random, -$length);
            }elseif(strlen($random) < $length){
                $rLen = $length - strlen($random);
                $random = $random . random_int(pow(10, $rLen - 1), pow(10, $rLen) - 1);
            }
        }

        return $prefix . $random;

    }

    public static function logException(\Throwable $e, ?string $prefix = '', array $extraArr = []){
        is_array($extraArr) or $extraArr = [$extraArr];
        $repArr = ["\r", "\n"];
        $extraData = $e->data ?? [];
        is_array($extraData) or $extraData = [$extraData];
        $extraArr = ArrayHelper::merge($extraData, $extraArr);

        $text = (string)$e;
        $ttArr = explode("\n", $text);

        $recordString = $prefix . '|' . $ttArr[0] . '|' . json_encode($extraArr, JSON_UNESCAPED_UNICODE) . '|' . str_replace($repArr, '  <-  ', $e->getTraceAsString());
        if($e instanceof DefaultException){
            \EasySwoole\EasySwoole\Logger::getInstance()->waring($recordString);
        }else{
            \EasySwoole\EasySwoole\Logger::getInstance()->error($recordString);
        }
    }

    /**
     * 校验是否手机号码
     * @param $phone
     * @return bool
     */
    public static function isPhone(string $phone){
        $reg = '/^((13[0-9])|(14[5,7,9])|(15[^4])|(18[0-9])|(19[0-9])|(17[0,1,3,5,6,7,8]))\\d{8}$/';//手机号码正则规则
        if(preg_match($reg,$phone)){
            return true;
        }else{
            return false;
        }
    }

    /**
     * 生成单号【1:订单号，2:付款交易单号】
     * @param int $midFix
     */
    public static function generalRandomNo(int $midFix = 0)
    {
        $string = date('YmdHis') . intval($midFix) . self::getRandom('', 5);
        return $string;
    }
    /**月份分表*/
    public static function setTableNameByMonth($modelObj, $month)
    {
        /**如果不是当前月,那么就去取分表数据*/
        $now_month = date('Ym');
        if(!empty($month) && $month < $now_month){
            /**判断当前月份分表是否存在*/
            $database_name = \EasySwoole\EasySwoole\Config::getInstance()->getConf('MYSQL_READ')['database'];
            $table_date_table_name = $modelObj::create()->getTableName() . '_' . $month;
            $sql = <<<sql

                    SELECT COUNT(*) `cnt` 
                    FROM information_schema.tables 
                    WHERE table_schema = '{$database_name}' 
                    AND table_name = '{$table_date_table_name}'
sql;
            $queryBuild = new QueryBuilder();
            $queryBuild->raw($sql);
            $result = DbManager::getInstance()->query($queryBuild, true, 'write');
            if($result->getResult()[0]['cnt'] == 1) {
                $modelObj = $modelObj->tableName($modelObj::create()->getTableName() . '_' . $month);
            }
        }
        return $modelObj;
    }

    /**按月份分表*/
    public static function archiveRecordMonth($table, $origin_table = null){
        $origin_table = $origin_table ?? $table;
        /**中转表*/
        $table_new = $table . '_new';
        /**判断上一个月的分布是否存在*/
        $last_month = date('Ym', strtotime(date('Y-m-01')) - 86400);
        $table_nonth = $table . '_' . $last_month;
        $query = new QueryBuilder();
        $sql = "SHOW TABLES LIKE '". $table_nonth ."'";
        $query->raw($sql);
        $result = DbManager::getInstance()->query($query, true, 'write');
        if ($result->getResult()) {
            return false;
//            throw new \Exception('分表已存在');
        }
        $first_day = date('Y-m-01');
        try {
            DbManager::getInstance()->startTransactionWithCount('write');
            $query = new QueryBuilder();
            /**创建一个新表用于中转数据*/
            $sql = "CREATE TABLE If Not Exists {$table_new} LIKE {$origin_table}";
            $query->raw($sql);
            DbManager::getInstance()->query($query, false, 'write');
            /**将本月新产生的数据写入到中专表*/
            $sql = "INSERT INTO {$table_new}
                    SELECT * FROM {$origin_table} WHERE created_at>='{$first_day}';";#DATE_FORMAT(CURDATE(), '%Y-%m-01')
            $query->raw($sql);
            DbManager::getInstance()->query($query, false, 'write');
            /**将表中本月的数据删掉*/
            $sql = "DELETE FROM {$origin_table} WHERE created_at>='{$first_day}';";
            $query->raw($sql);
            DbManager::getInstance()->query($query, false, 'write');
            /**将表名修改为月份分表*/
            $sql = "RENAME TABLE {$origin_table} TO {$table_nonth};";
            $query->raw($sql);
            DbManager::getInstance()->query($query, false, 'write');
            /**将中转表名修改为正式表名*/
            $sql = "RENAME TABLE {$table_new} TO {$origin_table};";
            $query->raw($sql);
            DbManager::getInstance()->query($query, false, 'write');

            DbManager::getInstance()->commitWithCount('write');
        }catch (\Exception $e){
            DbManager::getInstance()->rollbackWithCount('write');
            throw $e;
        }
        return true;
    }
    /**月份*/
    public static function getTableMonths()
    {
        $startMonth = '2024-07';
        /**结束月份为上一个月*/
//        $endMonth = date('Y-m-01', strtotime('-1 month'));
        $endMonth = date('Y-m', strtotime(date('Y-m-01')) - 86400);
        /**以开始月和结束月循环*/
        $months = [];
        while (strtotime($startMonth) <= strtotime($endMonth)) {
            $months[] = str_replace('-', '', $startMonth);
            $startMonth = date('Y-m', strtotime("+1 month", strtotime($startMonth)));
        }
        return $months;
    }
}
